package net.zoneland.knowledge.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Preconditions;
import net.zoneland.knowledge.dao.WorkApplyInfoMapper;
import net.zoneland.knowledge.model.TaskObjectWrapper;
import net.zoneland.knowledge.model.TaskPageObjectWrapper;
import net.zoneland.knowledge.model.WorkApplyInfo;
import net.zoneland.knowledge.service.UniFlowService;
import net.zoneland.knowledge.service.bean.ResultInfo;
import net.zoneland.knowledge.utils.WorkApplyOperationUtils;
import net.zoneland.uniflow.client.*;
import net.zoneland.uniflow.client.impl.SyncProcessServiceClient;
import net.zoneland.uniflow.client.impl.SyncTaskServiceClient;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 统一流程接口服务
 *
 * @version v1.0
 * @ClassName: UniflowServiceImpl
 * @Author: Pbody
 * @Date: 2020/3/23 17:45
 */
@Service
public class UniFlowServiceImpl implements UniFlowService {

    private static final Logger logger = LoggerFactory.getLogger(UniFlowServiceImpl.class);

    /**
     * 流程实例状态：运行
     */
    public static final int PROCESS_STATE_RUNNING = 1;

    /**
     * 流程实例状态：结束（正常流转结束）
     */
    public static final int PROCESS_STATE_END = 2;

    /**
     * 流程实例状态：停止（强行停止）
     */
    public static final int PROCESS_STATE_STOP = 3;

    /**
     * 流程实例状态：挂起（暂停）
     */
    public static final int PROCESS_STATE_SUSPEND = 4;

    /**
     * 流程决策业务操作.
     */
    @Autowired
    private SyncProcessServiceClient processClient;
    /**
     * 任务调度业务操作.
     */
    @Autowired
    private SyncTaskServiceClient taskClient;

    @Autowired
    private WorkApplyInfoMapper workApplyInfoMapper;

    /**
     * 流程服务关闭
     **/
    public void close() {
        processClient.close();
        taskClient.close();
    }

    /**
     * 启动流程
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:00 2020/5/19
     * @Param [businessProcess, applicant, applyId, applyTitle, submit, handlers, extendData]
     **/
    @Override
    public ResultInfo startProcess(String businessProcess, UserInfo applicant, String applyId, String applyTitle, boolean submit, UserInfo[] handlers, Map<String, String> extendData) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(processClient.startProcess(businessProcess, applicant, applyId,
                    applyTitle, submit, handlers, extendData));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>流程[{}]启动出错：[{}]", businessProcess, ex);
            result.setInfo("流程启动出错");
        }
        return result;
    }

    /**
     * 查询扩展参数
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:00 2020/5/19
     * @Param [piid, paramNames]
     **/
    @Override
    public ResultInfo queryExtendData(String piid, String[] paramNames) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(processClient.queryExtendData(piid, paramNames));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询流程[{}]扩展参数出错：[{}]", piid, ex);
            result.setInfo("查询扩展参数出错");
        }
        return result;
    }

    /**
     * 设置扩展参数
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:13 2020/5/19
     * @Param [piid, extendData]
     **/
    @Override
    public ResultInfo setExtendData(String piid, Map<String, String> extendData) {
        ResultInfo result = new ResultInfo();
        try {
            processClient.setExtendData(piid, extendData);
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>流程[{}],设置扩展参数出错：[{}]", piid, ex);
            result.setInfo("设置扩展参数出错");
        }
        return result;
    }

    /**
     * 删除流程
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:14 2020/5/19
     * @Param [piid, applicant, notion]
     **/
    @Override
    public ResultInfo deleteProcess(String piid, UserInfo applicant, String notion) {
        ResultInfo result = new ResultInfo();
        try {
            processClient.deleteProcess(piid, applicant, notion);
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>删除流程[{}]出错：[{}]", piid, ex);
            result.setInfo("删除流程出错");
        }
        return result;
    }

    /**
     * 任务处理
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:14 2020/5/19
     * @Param [tiid, handler, decision, option, notion, nextNode, nextHandlers, extendData, newApplyTitle]
     **/
    @Override
    public ResultInfo completeTask(String tiid, UserInfo handler, String decision, String option, String notion, String nextNode, UserInfo[] nextHandlers, Map<String, String> extendData, String newApplyTitle) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.completeTask(tiid, handler, decision, option, notion,
                    nextNode, nextHandlers, extendData, newApplyTitle));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>任务[{}]处理出错：[{}]", tiid, ex);
            result.setInfo("任务处理出错");
        }
        return result;
    }

    /**
     * 处理待办。
     *
     * @param tiid     任务实例id
     * @param handler  处理人
     * @param decision 决策选项
     * @param option   决策名称
     * @param notion   处理意见
     * @return 处理结果
     */
    @Override
    public ResultInfo completeTask(final String tiid, final UserInfo handler, final String decision, final String option, final String notion) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.completeTask(tiid, handler, decision, option, notion,null, null, null));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>任务[{}]处理出错", tiid, ex);
            result.setInfo("任务处理出错");
        }
        return result;
    }

    /**
     * 代办,已办任务查询
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:14 2020/5/19
     * @Param [param, page, pageSize]
     **/
    @Override
    public ResultInfo queryTasksByPage(final TaskQueryParam param, final int page, final int pageSize) {
        final ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.queryTasksByPage(param, page, pageSize));
            result.setResult(true);
        } catch (final ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>代办,已办任务[{}]查询出错：[{}]", JSON.toJSONString(param) , page, pageSize, ex);
            result.setInfo("任务查询出错");
        }
        return result;
    }

    @Override
    public ResultInfo queryTasksByPageForWorkCenter(final TaskQueryParam param, final int page, final int pageSize) {
        final ResultInfo result = new ResultInfo();
        try {
            final TaskPageObject taskPageObject = taskClient.queryTasksByPage(param, page, pageSize);
            final TaskPageObjectWrapper taskPageObjectWrapper = new TaskPageObjectWrapper();
            BeanUtils.copyProperties(taskPageObject, taskPageObjectWrapper);
            // 返回数据结构不变的情况下，新增增加特殊的工单状态字段
            final Set<String> applyIds = Arrays.stream(taskPageObject.getTaskObjects())
                    .map(TaskObject::getApplyId)
                    .collect(Collectors.toSet());

            final List<TaskObjectWrapper> tasks = Arrays.stream(taskPageObject.getTaskObjects())
                .map(item -> {
                    final TaskObjectWrapper target = new TaskObjectWrapper();
                    BeanUtils.copyProperties(item, target);
                    return target;
                })
                .collect(Collectors.toList());

            if (CollectionUtils.isNotEmpty(tasks)) {
                /*
                 * 主要处理两部分逻辑：
                 * 设置页面显示的工单状态：这个状态是业务系统自己维护的（跟流程平台的节点不一样），取工单的状态，需要查询
                 * 设置页面显示的拟搞时间：所有工单拟搞时间都是要取主工单的拟搞时间
                 */
                final List<WorkApplyInfo> workApplys = workApplyInfoMapper.workApplyInfoByApplyIdsForWorkCenterShow(applyIds);

                // 工单ID和工单信息Map，用于取状态
                final Map<String, WorkApplyInfo> applyIdWorkApplyMap = workApplys
                        .stream()
                        .collect(Collectors.toMap(WorkApplyInfo::getApplyId, Function.identity(), (a, b) -> b));

                // 工单ID和主工单map，用于取拟搞时间
                final Map<String, WorkApplyInfo> applyIdMainApplyInfoMap = getApplyIdMainApplyMap(workApplys);

                tasks.forEach(item -> {
                        // 设置工单状态
                        Optional.ofNullable(applyIdWorkApplyMap.get(item.getApplyId()))
                                .ifPresent(apply -> item.setStatus(apply.getStatus()));

                        // 设置拟稿时间
                        Optional.ofNullable(applyIdMainApplyInfoMap.get(item.getApplyId()))
                                .ifPresent(apply -> {
                                    item.setCreateTime(apply.getCreateTime().getTime());
                                    item.getApplicant().setUserName(apply.getCreatorName());
                                });
                    }
                );
                taskPageObjectWrapper.setTaskObjects(tasks.toArray(new TaskObjectWrapper[]{}));
            }
            result.setData(taskPageObjectWrapper);
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>代办,已办任务[{}]查询出错：[{}]", param, ex);
            result.setInfo("任务查询出错");
        }
        return result;
    }

    /**
     * 获取工单ID和主工单信息Map.
     * @param workApplys 工单列表
     * @return 工单ID与主工单信息Map
     */
    @Override
    public Map<String, WorkApplyInfo> getApplyIdMainApplyMap(final List<WorkApplyInfo> workApplys) {
        final Map<String, WorkApplyInfo> idAndWorkApplyMap = workApplys
                .stream()
                .collect(Collectors.toMap(WorkApplyInfo::getId, Function.identity(), (a, b) -> b));
        return workApplys.stream()
                .collect(Collectors.toMap(WorkApplyInfo::getApplyId,
                    // 如果主工单信息已经查询则在内存中取，否则从数据库查询
                    workApplyInfo ->
                        Optional.ofNullable(idAndWorkApplyMap.get(workApplyInfo.getMainWorkApplyId()))
                                .orElseGet(() -> workApplyInfoMapper.findWorkApplyInfoById(workApplyInfo.getMainWorkApplyId())),
                    (a, b) -> b));
    }

    /**
     * 待阅,已阅任务查询
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:21 2020/5/19
     * @Param [param, page, pageSize]
     **/
    @Override
    public ResultInfo queryReadsByPage(ReadQueryParam param, int page, int pageSize) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.queryReadsByPage(param, page, pageSize));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>待阅,已阅[{}]任务查询出错：[{}]", param, ex);
            result.setInfo("任务查询出错");
        }
        return result;
    }

    /**
     * 查询任务决策
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:23 2020/5/19
     * @Param [systemId, businessProcess, node]
     **/
    @Override
    public ResultInfo queryTaskDecisions(String systemId, String businessProcess, String node) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.queryTaskDecisions(systemId, businessProcess, node));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询任务决策[{}]出错：[{}]", node, ex);
            result.setInfo("查询任务决策出错");
        }
        return result;
    }

    /**
     * 获取审批
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:23 2020/5/19
     * @Param [tiid]
     **/
    @Override
    public ResultInfo getApprove(String tiid) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.getApprove(tiid));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>获取审批[{}]出错：[{}]", tiid, ex);
            result.setInfo("获取审批出错");
        }
        return result;
    }

    /**
     * 根据任务实例ID查询任务
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:25 2020/5/19
     * @Param [tiid]
     **/
    @Override
    public ResultInfo getTask(String tiid) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.getTask(tiid));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>根据任务实例ID[{}]查询任务出错：[{}]", tiid, ex);
            result.setInfo("任务实例查询出错");
        }
        return result;
    }

    /**
     * 流程任务转交
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:35 2020/5/19
     * @Param [tiid, nextHandlers, option, notion]
     **/
    @Override
    public ResultInfo transferTask(String tiid, UserInfo[] nextHandlers, String option, String notion) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.transferTask(tiid, nextHandlers, option, notion));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>任务[{}]转交出错：[{}]", tiid, ex);
            result.setInfo("任务转交出错。" + ex.getMessage());
        }
        return result;
    }

    /**
     * 查询能否退回
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:36 2020/5/19
     * @Param [tiid]
     **/
    @Override
    public ResultInfo canReject(String tiid) {
        ResultInfo result = new ResultInfo();
        try {
            result.setResult(taskClient.canReject(tiid));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询[{}]能否退回出错：[{}]", tiid, ex);
            result.setInfo("查询能否退回出错");
        }
        return result;
    }

    /**
     * 查询流程实例
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:36 2020/5/19
     * @Param [processQueryParam]
     **/
    @Override
    public ResultInfo queryProcessInstances(ProcessQueryParam processQueryParam) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(processClient.queryProcessInstances(processQueryParam));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询[{}]流程实例出错：[{}]", processQueryParam, ex);
            result.setInfo("查询流程实例出错");
        }
        return result;
    }

    /**
     * 分页查询流程实例.
     *
     * @param param    查询参数
     * @param page     当前页
     * @param pageSize 每页记录数
     * @return 流程实例结果
     */
    @Override
    public ResultInfo queryProcessInstancesByPage(ProcessQueryParam param, Integer page, Integer pageSize) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(processClient.queryProcessInstancesByPage(param, page, pageSize));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询[{}]流程实例出错：[{}]", param, ex);
            result.setInfo("查询流程实例出错");
        }
        return result;
    }

    /**
     * 创建阅办
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:37 2020/5/19
     * @Param [piid, node, owners, readUrl, readTitle, extendData, creator, additionFlag]
     **/
    @Override
    public ResultInfo createReads(String piid, String node, UserInfo[] owners, String readUrl,
                                  String readTitle, Map<String, String> extendData, UserInfo creator, int additionFlag) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.createReads(piid, node, owners, readUrl,
                    readTitle, extendData, creator, additionFlag));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>创建[{}]阅办出错：[{}]", piid, ex);
            result.setInfo("创建阅办出错");
        }
        return result;
    }

    /**
     * 获取阅读项
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:38 2020/5/19
     * @Param [riid]
     **/
    @Override
    public ResultInfo getRead(String riid) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.getRead(riid));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>获取[{}]阅读项出错：[{}]", riid, ex);
            result.setInfo("获取阅读项出错");
        }
        return result;
    }

    /**
     * 完成阅办
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:40 2020/5/19
     * @Param [riid, notion]
     **/
    @Override
    public ResultInfo completeRead(String riid, String notion) {
        ResultInfo result = new ResultInfo();
        try {
            taskClient.completeRead(riid, notion);
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>完成[{}]阅办出错：[{}]", riid, ex);
            result.setInfo("完成阅办出错");
        }
        return result;
    }

    /**
     * 查询任务扩展数据
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:53 2020/5/19
     * @Param [tiid, extendParamNames]
     **/
    @Override
    public ResultInfo queryTaskExtendData(String tiid, String[] extendParamNames) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.queryTaskExtendData(tiid, extendParamNames));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询任务[{}]扩展数据出错：[{}]", tiid, ex);
            result.setInfo("查询任务扩展数据出错");
        }
        return result;
    }

    /**
     * 设置阅读扩展数据
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:54 2020/5/19
     * @Param [riid, extendData]
     **/
    @Override
    public ResultInfo setReadExtendData(String riid, Map<String, String> extendData) {
        ResultInfo result = new ResultInfo();
        try {
            taskClient.setReadExtendData(riid, extendData);
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>设置任务[{}]扩展数据[{}]出错：[{}]", riid, extendData, ex);
            result.setInfo("设置任务扩展数据出错");
        }
        return result;
    }

    /**
     * 查询阅读扩展数据
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:55 2020/5/19
     * @Param [riid, extendParamNames]
     **/
    @Override
    public ResultInfo queryReadExtendData(String riid, String[] extendParamNames) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.queryReadExtendData(riid, extendParamNames));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询[{}]阅读扩展数据出错：[{}]", riid, ex);
            result.setInfo("查询阅读扩展数据出错");
        }
        return result;
    }

    /**
     * 查询前任务扩展数据
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:55 2020/5/19
     * @Param [tiid, extendParamNames]
     **/
    @Override
    public ResultInfo queryPreTaskExtendData(String tiid, String[] extendParamNames) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.queryPreTaskExtendData(tiid, extendParamNames));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询前任务[{}]扩展数据出错：[{}]", tiid, ex);
            result.setInfo("查询前任务扩展数据出错");
        }
        return result;
    }

    /**
     * 设置任务扩展数据
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 18:56 2020/5/19
     * @Param [tiid, extendData]
     **/
    @Override
    public ResultInfo setTaskExtendData(String tiid, Map<String, String> extendData) {
        ResultInfo result = new ResultInfo();
        try {
            taskClient.setTaskExtendData(tiid, extendData);
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>设置任务[{}]扩展数据[{}]出错：[{}]", tiid, extendData, ex);
            result.setInfo("设置任务扩展数据出错");
        }
        return result;
    }

    /**
     * 流程退回
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 17:55 2020/5/19
     * @Param [tiid, option, notion, extendData]
     **/
    @Override
    public ResultInfo reject(String tiid, String option, String notion, Map<String, String> extendData) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.reject(tiid, option, notion, extendData));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>退回[{}]出错：[{}]", tiid, ex);
            result.setInfo("退回出错");
        }
        return result;
    }

    /**
     * 查询流程模板节点
     *
     * @return net.zoneland.auditCloud.biz.result.ResultInfo
     * @Author Pbody
     * @Date 17:55 2020/5/19
     * @Param [businessProcess, templateVer, nodeId]
     **/
    @Override
    public ResultInfo queryProcessTemplateNode(String businessProcess, int templateVer, String nodeId) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(processClient.queryProcessTemplateNode(businessProcess, templateVer, nodeId));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询流程模板[{}]节点出错：[{}]", businessProcess, ex);
            result.setInfo("查询流程模板节点出错");
        }
        return result;
    }

    /**
     * 查询业务流程信息
     *
     * @param systemId
     * @param businessProcess
     * @param bizCategory
     * @param roles
     * @param keywords
     * @param provinceId
     * @return ResultInfo
     * @Param systemId 系统标识
     * @Param businessProcess 业务流程名称
     * @Param bizCategory 业务种类
     * @Param roles 角色
     * @Param keywords 关键字
     * @Param provinceId 省份编号
     */
    @Override
    public ResultInfo queryBusinessProcess(String systemId, String templateId, String businessProcess, String bizCategory, String[] roles, String[] keywords, String provinceId) {
        final ResultInfo result = new ResultInfo();
        try {
            result.setData(processClient.queryBusinessProcess(systemId, templateId, businessProcess, bizCategory, roles, keywords, provinceId));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询业务流程[{}]出错：[{}]", bizCategory, ex);
            result.setInfo("查询业务流程出错");
        }
        return result;
    }

    /**
     * 发起并跳转流程.
     * @param businessProcess 流程模板编号
     * @param applicant 申请人
     * @param applyId 工单号
     * @param applyTitle 工单名称
     * @param jumpNode 节点id
     * @param nodeHandlers 节点管理员
     * @param extendData 额外阐述
     * @return 任务节点信息
     */
    @Override
    public ResultInfo startAndJumpTask(String businessProcess, UserInfo applicant, String applyId, String applyTitle, String jumpNode, UserInfo[] nodeHandlers, Map<String, String> extendData) throws Exception {
        ResultInfo result = new ResultInfo();

        try {
            result.setData(processClient.startProcessAndJump2Node(businessProcess, applicant, applyId, applyTitle, jumpNode, nodeHandlers, extendData));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>任务处理出错:", ex);
            result.setInfo("任务处理出错");
        }
        return result;
    }

    /**
     * 根据工单号查询待办任务集合.
     * @param business 业务流程
     * @param applyId 工单号
     * @return 任务集合
     */
    @Override
    public ResultInfo queryTasksByApplyId(String business, String applyId) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.getReadyTasksByApplyId(business,applyId));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>根据工单号获取任务出错:", ex);
            result.setInfo("根据工单号获取任务出错");
        }
        return result;
    }

    /**
     * 恢复任务.
     *
     * @param tiid 任务id
     * @return 结果
     */
    @Override
    public ResultInfo resumeTask(final String tiid) {
        ResultInfo result = new ResultInfo();
        try {
            taskClient.resumeTask(tiid);
            TaskObject task = taskClient.getTask(tiid);
            result.setData(new TaskObject[] { task });
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>恢复任务出错，tiid:{}", tiid, ex);
            result.setInfo("恢复任务出错");
        }
        return result;
    }

    /**
     * 恢复任务.
     *
     * @param tiid 任务id
     * @return 结果
     */
    @Override
    public ResultInfo suspendTask(final String tiid) {
        ResultInfo result = new ResultInfo();
        try {
            taskClient.suspendTask(tiid);
            TaskObject task = taskClient.getTask(tiid);
            result.setData(new TaskObject[] { task });
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>挂起任务出错，tiid:{}", tiid, ex);
            result.setInfo("挂起任务出错");
        }
        return result;
    }

    /**
     * 撤回任务.
     *
     * @param tiid   任务id
     * @param notion 撤回原因
     * @return 结果
     */
    @Override
    public ResultInfo revokeTask(final String tiid, final String notion) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.revoke(tiid, null, notion, null));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>撤回任务出错，tiid:{}", tiid, ex);
            result.setInfo("撤回任务出错");
        }
        return result;
    }

    /**
     * 复活流程.
     *
     * @param piid         业务流程id
     * @param nodeId       节点编号
     * @param nodeHandlers 节点处理人
     * @param applicant    发起人
     * @param notion       原因
     * @return 结果
     */
    @Override
    public ResultInfo syncReliveProcess(String piid, String nodeId, UserInfo[] nodeHandlers, UserInfo applicant, String notion) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(processClient.reliveProcess(piid, nodeId, nodeHandlers, applicant, notion));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>复活流程出错，piid:{}, nodeId:{}", piid, nodeId, ex);
            result.setInfo("复活流程出错");
        }
        return result;
    }

    /**
     * 将工单发送指定环节上去.
     * <p>其逻辑在于，通过业务驱动流程，而忽略流程本身的流转规则（即流程图上的决策）</p>
     * <p>其实现方式为，在途流程通过跳转达到目标环节；已结束流程通过复活达到目标环节</p>
     * @param workApplyInfo 要发送的工单对象
     * @param nodeId 要发送到的节点编号
     * @param nodeHandlers 节点处理人
     * @param applicant 发起人
     * @param notion 原因
     * @return 结果
     */
    @Override
    public ResultInfo sendTheApplyToNode(final WorkApplyInfo workApplyInfo, final String nodeId, final UserInfo[] nodeHandlers, final UserInfo applicant, final String notion) {
        ResultInfo result = new ResultInfo();
        TaskObject[] taskObjects = null;

        ProcessQueryParam param = new ProcessQueryParam();
        param.setBusinessProcesses(new String[] { workApplyInfo.getFlowType()});
        String uniflowApplyId = WorkApplyOperationUtils.getUniflowApplyId(workApplyInfo);
        param.setApplyId(uniflowApplyId);
        try {
            // 先查找流程实例
            ProcessObject[] processObjects = processClient.queryProcessInstances(param);
            // 有且只有一个结果
            Preconditions.checkArgument(ArrayUtils.getLength(processObjects) == 1, "根据工单号[%s]查找流程实例得到[%s]个结果, 工单ID：[%s]，工单类型：[%s]",
                    uniflowApplyId, ArrayUtils.getLength(processObjects), workApplyInfo.getId(), workApplyInfo.getFlowType());
            ProcessObject processObject = processObjects[0];
            String piid = processObject.getPiid();
            int state = processObject.getState();

            // 开始驱动流程（这个系统的特点是：业务驱动流程，业务说去哪就去哪，而不是流程驱动业务，因为业务规则太复杂、无法通过流程设计来表达）
            if(state == PROCESS_STATE_RUNNING || state == PROCESS_STATE_SUSPEND) {
                // 流程在还运行状态，直接跳转到目标节点
                taskObjects = taskClient.jump(piid, nodeId, nodeHandlers, applicant, null, notion, null);
            } else if(state == PROCESS_STATE_END || state == PROCESS_STATE_STOP) {
                // 流程已经结束，那就复活到指定节点
                taskObjects = processClient.reliveProcess(piid, nodeId, nodeHandlers, applicant, notion);
            } else {
                // it's impossible……
                throw new ProcessServiceException(String.format("The ProcessInstance[%s]'s state exceeds expectations[1,2,3,4], the state actually is [%d]", piid, state));
            }
            result.setData(taskObjects);
            result.setResult(true);
        } catch (ProcessServiceException e) {
            logger.error("流程发送时出现流程异常，workApplyInfo.id:{}, nodeId:{}, handle:{}", workApplyInfo.getId(), nodeId, applicant.getUserId(), e);
            result.setInfo("流程发送时出现流程异常");
        }
        return result;
    }

    /**
     * 跳转任务.
     *
     * @param tiid         任务实例id
     * @param nodeId       节点编号
     * @param nodeHandlers 节点处理人
     * @param handler      当前处理人（发起跳转的人）
     * @param notion       原因
     * @return 结果
     */
    @Override
    public ResultInfo jump(final String tiid, final String nodeId, final UserInfo[] nodeHandlers, final UserInfo handler, final String notion) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.nodeJump(tiid, nodeId, nodeHandlers, handler, notion, notion, null));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>跳转任务出错，tiid:{}, nodeId:{}", tiid, nodeId, ex);
            result.setInfo("跳转任务出错");
        }
        return result;
    }

    /**
     * 流程跳转.
     *
     * @param piid         流程实例id
     * @param nodeId       节点编号
     * @param nodeHandlers 节点处理人
     * @param handler      当前处理人（发起跳转的人）
     * @param decision      决策
     * @param notion       原因
     * @return 结果
     */
    @Override
    public ResultInfo jumpToNode(final String piid, final String nodeId, final UserInfo[] nodeHandlers, final UserInfo handler, String decision, final String notion) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.jump(piid, nodeId, nodeHandlers, handler, notion, notion, null));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>跳转流程出错，piid:{}, nodeId:{}", piid, nodeId, ex);
            result.setInfo("跳转任务出错");
        }
        return result;
    }

    @Override
    public ResultInfo stopProcess(final String piid, final UserInfo applicant, final String notion) {
        ResultInfo result = new ResultInfo();
        try {
            result.setData(processClient.stopProcess(piid, applicant, notion));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>停止流程出错，piid:{}", piid, ex);
            result.setInfo("跳转任务出错");
        }
        return result;
    }

    /**
     * 查询阅办
     *
     * @param param 参数
     * @return result
     */
    @Override
    public ResultInfo queryReads(ReadQueryParam param) {
        final ResultInfo result = new ResultInfo();
        try {
            result.setData(taskClient.queryReads(param));
            result.setResult(true);
        } catch (ProcessServiceException ex) {
            logger.error(">>>>>>>>>>>查询[{}]阅办出错：[{}]", param, ex);
            result.setInfo("查询阅办出错");
        }
        return result;
    }

}
